﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SortableBindingList.cs" company="Elilink">
//   Defor
// </copyright>
// <summary>
//   Class SortableBindingList
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace BusinessLayer.Tools
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;

    /// <summary>
    ///     Class SortableBindingList
    /// </summary>
    /// <typeparam name="T">Type of objects being added to sortable list.</typeparam>
    public class SortableBindingList<T> : BindingList<T>
    {
        #region Fields

        /// <summary>
        ///     The _comparers
        /// </summary>
        private readonly Dictionary<Type, PropertyComparer<T>> _comparers;

        /// <summary>
        ///     The _is sorted
        /// </summary>
        private bool _isSorted;

        /// <summary>
        ///     The _list sort direction
        /// </summary>
        private ListSortDirection _listSortDirection;

        /// <summary>
        ///     The _property descriptor
        /// </summary>
        private PropertyDescriptor _propertyDescriptor;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="SortableBindingList{T}" /> class.
        /// </summary>
        public SortableBindingList()
            : base(new List<T>())
        {
            _comparers = new Dictionary<Type, PropertyComparer<T>>();
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="SortableBindingList{T}" /> class.
        /// </summary>
        /// <param name="enumeration">
        ///     The enumeration.
        /// </param>
        public SortableBindingList(IEnumerable<T> enumeration)
            : base(new List<T>(enumeration))
        {
            _comparers = new Dictionary<Type, PropertyComparer<T>>();
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets a value indicating whether this instance is sorted core.
        /// </summary>
        /// <value><c>true</c> if this instance is sorted core; otherwise, <c>false</c>.</value>
        protected override bool IsSortedCore
        {
            get
            {
                return _isSorted;
            }
        }

        /// <summary>
        ///     Gets the sort direction core.
        /// </summary>
        /// <value>The sort direction core.</value>
        protected override ListSortDirection SortDirectionCore
        {
            get
            {
                return _listSortDirection;
            }
        }

        /// <summary>
        ///     Gets the sort property core.
        /// </summary>
        /// <value>The sort property core.</value>
        protected override PropertyDescriptor SortPropertyCore
        {
            get
            {
                return _propertyDescriptor;
            }
        }

        /// <summary>
        ///     Gets a value indicating whether [supports searching core].
        /// </summary>
        /// <value><c>true</c> if [supports searching core]; otherwise, <c>false</c>.</value>
        protected override bool SupportsSearchingCore
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        ///     Gets a value indicating whether [supports sorting core].
        /// </summary>
        /// <value><c>true</c> if [supports sorting core]; otherwise, <c>false</c>.</value>
        protected override bool SupportsSortingCore
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        ///     Applies the sort core.
        /// </summary>
        /// <param name="property">
        ///     The property.
        /// </param>
        /// <param name="direction">
        ///     The direction.
        /// </param>
        protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
        {
            var itemsList = (List<T>)Items;

            Type propertyType = property.PropertyType;
            PropertyComparer<T> comparer;
            if (!_comparers.TryGetValue(propertyType, out comparer))
            {
                comparer = new PropertyComparer<T>(property, direction);
                _comparers.Add(propertyType, comparer);
            }

            comparer.SetPropertyAndDirection(property, direction);
            itemsList.Sort(comparer);

            _propertyDescriptor = property;
            _listSortDirection = direction;
            _isSorted = true;

            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        /// <summary>
        ///     Finds the core.
        /// </summary>
        /// <param name="property">
        ///     The property.
        /// </param>
        /// <param name="key">
        ///     The key.
        /// </param>
        /// <returns>
        ///     Integer representation of comparer
        /// </returns>
        protected override int FindCore(PropertyDescriptor property, object key)
        {
            int count = Count;
            for (int i = 0; i < count; ++i)
            {
                T element = this[i];
                object value = property.GetValue(element);
                if (value == null)
                {
                    return -1;
                }

                if (value.Equals(key))
                {
                    return i;
                }
            }

            return -1;
        }

        /// <summary>
        ///     Removes the sort core.
        /// </summary>
        protected override void RemoveSortCore()
        {
            _isSorted = false;
            _propertyDescriptor = base.SortPropertyCore;
            _listSortDirection = base.SortDirectionCore;

            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        #endregion
    }
}